//
//  Expression.swift
//  SLDatabase
//
//  Created by 姚胜龙 on 2017/7/6.
//  Copyright © 2017年 姚胜龙. All rights reserved.
//

import UIKit

/**
 * 表达式类->用来构建SQL语句, 所有的SQL语句都是表达式
 * 表达式的三种类型：1、操作类型（创建表、删除表、查询表...）
                  2、数据类型（Int类型、可选类型、Double类型...）
                  3、字段类型（表的字段）

 * 面向协议的编程！！！！
 */

//祖宗类
//所有表达式->高度抽象的表达式类型
public protocol Expressible {

    //在这里写一个伏笔，以后通过会拼接成所有的SQL -> 合并表达式
    //高度抽象为一个表达式->SQL语句-> 最后遍历expression对象 -> 构建SQL语句
    var expression:Expression<Void> { get }
}

//给高度抽象表达式扩展，将expression转换成一个SQL
extension Expressible {
    func asSQL() -> String {
        let sql = expression
        var index = 0
        return sql.template.characters.reduce("") { template, character in
            let append:String

            if character == "?" {
                //拼接“？”
                append = transcode(sql.bindings[index])
                index += 1
            }
            else {
                //拼接关键字
                append = String(character)
            }
            return template + append
        }
    }
}


//------------------定义具体抽象字段表达式-------------------------------

//父类
//定义具体抽象字段表达式 -> 字段抽象表达式 t_user [t_user_name NOT NULL]
public protocol ExpressionType: Expressible {
    //具体字段抽象
    //抽象一：数据类型 -> (Int、Int32、Double...)
    //在这里并不知道具体是什么类型？ 采用 -> 泛型设计

    associatedtype UnderlyingType = Void //这个未知的类型默认为Void类型

    //抽象二 模板（例如：表名称、字段名称...） 具体是"表名称" 还是 "字段名称" 并不知道，所以用一个模板表示
    var template:String { get }

    //抽象三 绑定参数（表字段->参数列表、约束条件(NOT NULL 、DEFAULT、PRIMERY KEY...)）
    //保存：表字段描述信息["类型" "NOT NULL" ...]
    var bindings:[Binding?] { get }

    //抽象四 构造方法
    //作用：初始化这些参数：template、bindings
    init(_ template:String, _ bindings:[Binding?])

}

extension ExpressionType {
    //给你个默认的表达式
    public var expression: Expression<Void> {
        return Expression(template, bindings)
    }
}

//处理特殊字段，给字段加入特殊字符
//例如 create table t_user (user_name text, user_sex text)//这是一种写法
//create table "t_user" ("user_name" text, "user_sex" text)//这是另一种写法
//这两种写法都可以，所以也要支持字符串方式的写法
extension ExpressionType {
    //扩展功能：能够支持普通文本类型的构造
    //写法一： 不加 " " 号
    public init(literal:String) {
        self.init(literal, [])
    }

    //写法二 加 " " 号
    //create table "t_user" ("user_name" text, "user_sex" text)
    public init(_ identifier:String) {
        self.init(literal: identifier.quote())
    }
}

//字段的表达式类型 -> 结构体 -> 子类
public struct Expression<DataType>: ExpressionType {

    //指定具体的类型 <泛型>
    public typealias UnderlyingType = DataType
    //继承模板
    public var template: String
    //继承表字段
    public var bindings: [Binding?]
    //继承构造方法
    public init(_ template:String, _ bindings:[Binding?]) {
        self.template = template
        self.bindings = bindings
    }

}

//------------------- 定义具体抽象操作表达式-----------------
//抽出一个文件 Query.swift

//定义具体抽象字段类型表达式

//-----让表达式（Expressible）成为可选类型，也就是说可以为nil --------
//因为数据库的字段是不用指定为空的，默认为空，所以我们要让我们的字段为可选类型

//这里需要自定义可选类型，因为还要扩展功能->表字段可以为空 -> Expressible也变成可选型
//因为系统的Optional无法满足，需要对系统的可选类型扩展
// 重点是处理表字段

public protocol OptionalType {
    //因为可选类型不确定是什么类型 -> 泛型
    associatedtype WrappedType
}

extension Optional: OptionalType {
    //给WrappedType去一个别名
    public typealias WrappedType = Wrapped
}


//约束一 （约束的是数字类型）
//扩展抽象->表字段表达式-> ExpressionType
//泛型约束条件 UnderlyingType必须是Value的子类
extension ExpressionType where UnderlyingType: Value {
    //表字段值
    //insert into t_user(name, sex) values (?,?)
    public init(value: UnderlyingType) {
        //字段的值，对应问号
        self.init("?", [value.dataTypeValue])
    }
}

//约束二 约束的是可选类型（ExpressionType）->(约束的是当前框架所能支持的类型，其他类型不允许传)
//UnderlyingType指定的数据类型是可选类型 并且可选类型还得是Value
//如果传进来的类型不是框架支持的类型，就相当于瞎搞
extension ExpressionType where UnderlyingType: OptionalType,UnderlyingType.WrappedType: Value {
    //可以为空
    public static var null: Self {
        return self.init(value:nil)
    }

    //重写可选类型方法
    public init(value: UnderlyingType.WrappedType?) {
        self.init("?", [value?.dataTypeValue])
    }
}

//还需要扩展可选类型 
extension Value {
    public var expression: Expression<Void> {
        //最终的表达式，将多个表达式合并成一个
        return Expression(value:self).expression
    }
}



